﻿using Bricks.Injecting;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Bricks
{
    
    /// <summary>
    /// 
    /// </summary>
    public static class ObjectExtensions
    {

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="instance"></param>
        /// <returns></returns>
        public static T As<T>(this object instance) where T : class
        {
            return instance as T;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="instance"></param>
        /// <returns></returns>
        public static bool Is<T>(this object instance)
        {
            return instance is T;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TTarget"></typeparam>
        /// <param name="source"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        public static TTarget Map<TSource, TTarget>(this TSource source, Action<TSource, TTarget> action)
        {
            return source.Map<TSource, TTarget>(ServiceLocator.Current.Resolve<TTarget>, action);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TTarget"></typeparam>
        /// <param name="source"></param>
        /// <param name="target"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        public static TTarget Map<TSource, TTarget>(this TSource source, TTarget target, Action<TSource, TTarget> action)
        {
            return source.Map<TSource, TTarget>(() => target, action);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TTarget"></typeparam>
        /// <param name="source"></param>
        /// <param name="targetAccessor"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        public static TTarget Map<TSource, TTarget>(this TSource source, Func<TTarget> targetAccessor, Action<TSource, TTarget> action)
        {
            TTarget target = targetAccessor.Invoke();

            action.Invoke(source, target);

            return target;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TTarget"></typeparam>
        /// <param name="source"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        public static IEnumerable<TTarget> Map<TSource, TTarget>(this IEnumerable<TSource> source, Action<TSource, TTarget> action)
        {
            return source.InnerMap(action).ToArray();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TTarget"></typeparam>
        /// <param name="source"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        private static IEnumerable<TTarget> InnerMap<TSource, TTarget>(this IEnumerable<TSource> source, Action<TSource, TTarget> action)
        {
            foreach (TSource instance in source)
            {
                yield return instance.Map<TSource, TTarget>(action);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TTarget"></typeparam>
        /// <param name="source"></param>
        /// <param name="targetAccessor"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        public static IEnumerable<TTarget> Map<TSource, TTarget>(this IEnumerable<TSource> source, Func<TTarget> targetAccessor, Action<TSource, TTarget> action)
        {
            return source.InnerMap(action).ToArray();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TTarget"></typeparam>
        /// <param name="source"></param>
        /// <param name="targetAccessor"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        private static IEnumerable<TTarget> InnerMap<TSource, TTarget>(this IEnumerable<TSource> source, Func<TTarget> targetAccessor, Action<TSource, TTarget> action)
        {
            foreach (TSource instance in source)
            {
                yield return instance.Map<TSource, TTarget>(targetAccessor, action);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEnum"></typeparam>
        /// <param name="enum"></param>
        /// <returns></returns>
        public static TEnum ToEnum<TEnum>(this string @enum) where TEnum : struct, IComparable, IFormattable, IConvertible
        {
            return (TEnum)Enum.Parse(typeof(TEnum), @enum);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEnum"></typeparam>
        /// <param name="enum"></param>
        /// <returns></returns>
        public static TEnum? TryToEnum<TEnum>(this string @enum) where TEnum : struct, IComparable, IFormattable, IConvertible
        {
            try
            {
                return @enum.ToEnum<TEnum>();
            }
            catch
            {
                return null;
            }
        }
    }
}
